package itsm.isperp.workflow.utils;

import itsm.isperp.framework.utils.Dom4jUtils;
import itsm.isperp.workflow.graph.DivShape;
import itsm.isperp.workflow.graph.ShapeMeta;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.tree.DefaultElement;
import org.dom4j.tree.DefaultText;

/**
 * 
 * 流程工具
 * 
 * @author lizx
 * @date 2013-4-24
 */
public final class BpmUtils {

	/**
	 * 将流程定义的xml文件转换为图形摘要
	 * 
	 * @param xml
	 *            流程定义的xml文件
	 * @return 流程摘要
	 * @throws DocumentException
	 *             文档异常
	 */
	public static ShapeMeta transGraph(String xml) throws DocumentException {
		xml = xml.replace(
				"xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"", "");
		Document doc = Dom4jUtils.loadXml(xml);
		Element root = doc.getRootElement();
		List<?> list = root.selectNodes("//bpmndi:BPMNShape");
		int minx = 0x186a0;
		int miny = 0x186a0;
		int maxw = 0;
		int maxh = 0;
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < list.size(); i++) {
			Element el = (Element) list.get(i);
			Element tmp = (Element) el.selectSingleNode("omgdc:Bounds");
			int x = (int) Float.parseFloat(tmp.attributeValue("x"));
			int y = (int) Float.parseFloat(tmp.attributeValue("y"));
			int w = x + (int) Float.parseFloat(tmp.attributeValue("width"));
			int h = y + (int) Float.parseFloat(tmp.attributeValue("height"));
			minx = Math.min(x, minx);
			miny = Math.min(y, miny);
			maxw = Math.max(w, maxw);
			maxh = Math.max(h, maxh);
		}

		List<?> pointList = root.selectNodes("//omgdi:waypoint");
		for (int i = 0; i < pointList.size(); i++) {
			Element tmp = (Element) pointList.get(i);
			int x = (int) Float.parseFloat(tmp.attributeValue("x"));
			int y = (int) Float.parseFloat(tmp.attributeValue("y"));
			minx = Math.min(x, minx);
			miny = Math.min(y, miny);
		}

		for (int i = 0; i < list.size(); i++) {
			Element el = (Element) list.get(i);
			Element tmp = (Element) el.selectSingleNode("omgdc:Bounds");
			int x = (int) Float.parseFloat(tmp.attributeValue("x"));
			int y = (int) Float.parseFloat(tmp.attributeValue("y"));
			int w = (int) Float.parseFloat(tmp.attributeValue("width"));
			int h = (int) Float.parseFloat(tmp.attributeValue("height"));
			x = (x - minx) + 5;
			y = (y - miny) + 5;
			String id = el.attributeValue("bpmnElement");
			Element procEl = (Element) root
					.selectSingleNode((new StringBuilder())
							.append("//process/descendant::*[@id='").append(id)
							.append("']").toString());
			String type = procEl.getName();
			if (type.equals("serviceTask")) {
				String attribute = procEl.attributeValue("class");
				if (attribute != null)
					if (attribute
							.equals("itsm.isperp.workflow.listener.MessageServiceTaskListener"))
						type = "email";
					else if (attribute
							.equals("itsm.isperp.service.bpm.ScriptTask"))
						type = "script";
			}
			Element multiObj = procEl
					.element("multiInstanceLoopCharacteristics");
			if (multiObj != null)
				type = "multiUserTask";
			Element parent = procEl.getParent();
			String name = procEl.attributeValue("name");
			int zIndex = 10;
			String parentName = parent.getName();
			if (parentName.equals("subProcess"))
				zIndex = 11;
			if (name == null)
				name = "";
			DivShape shape = new DivShape(name, x, y, w, h, zIndex, id, type);
			sb.append(shape);
		}

		ShapeMeta shapeMeta = new ShapeMeta(maxw, maxh, sb.toString());
		return shapeMeta;
	}

	public static String setMultiUserTaskCondition(final String sourceNode,
			String strLoop, String strComp, String defXml)
			throws DocumentException {

		/*
		 * <multiInstanceLoopCharacteristics isSequential="false">
		 * <loopCardinality>${signAllCount}</loopCardinality>
		 * <completionCondition>${nrOfCompletedInstances/nrOfInstances == 1
		 * }</completionCondition> </multiInstanceLoopCharacteristics>
		 */
		defXml = defXml.replace(
				"xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"",
				"xmlns:bpm=\"isperp\"");
		Document doc = Dom4jUtils.loadXml(defXml, "UTF-8");

		Element root = doc.getRootElement();
		DefaultElement nodeUserTask = (DefaultElement) root
				.selectSingleNode("//userTask[@id='" + sourceNode + "']");

		Element loopNode = ((DefaultElement) nodeUserTask)
				.element("multiInstanceLoopCharacteristics");

		if (loopNode == null) {

			Element mEl = nodeUserTask
					.addElement("multiInstanceLoopCharacteristics");
			mEl.addAttribute("isSequential", "false");

			if (StringUtils.isNotEmpty(strLoop)) {
				Element elAdd = mEl.addElement("loopCardinality");
				elAdd.add(new DefaultText(strLoop));
			}

			if (StringUtils.isNotEmpty(strComp)) {
				Element elAdd = mEl.addElement("completionCondition");
				elAdd.add(new DefaultText(strComp));
			}

		} else {
			Element loopExp = loopNode.element("loopCardinality");

			if (loopExp != null)
				loopNode.remove(loopExp);
			if (StringUtils.isNotEmpty(strLoop)) {
				Element elAdd = loopNode.addElement("loopCardinality");
				elAdd.setText(strLoop);
			}

			Element compExp = loopNode.element("completionCondition");
			if (compExp != null)
				loopNode.remove(compExp);
			if (StringUtils.isNotEmpty(strComp)) {
				Element elAdd = loopNode.addElement("completionCondition");
				elAdd.add(new DefaultText(strComp));
			}

		}
		String outXml = doc.asXML();
		outXml = outXml.replace("xmlns:bpm=\"isperp\"",
				"xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"");
		return outXml;

	}

	public static String setCondition(final String sourceNode,
			Map<String, String> map, String defXml) throws DocumentException {
		defXml = defXml.replace(
				"xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"",
				"xmlns:bpm=\"isperp\"");
		Document doc = Dom4jUtils.loadXml(defXml, "UTF-8");

		Element root = doc.getRootElement();
		List<Element> nodes = root.selectNodes("//sequenceFlow[@sourceRef='"
				+ sourceNode + "']");
		for (Element el : nodes) {
			String id = el.attributeValue("id");
			String condition = (String) map.get(id);

			Element conditionEl = el.element("conditionExpression");
			if (conditionEl != null)
				el.remove(conditionEl);
			if (StringUtils.isNotEmpty(condition)) {
				Element elAdd = el.addElement("conditionExpression");
				elAdd.addAttribute("xsi:type", "tFormalExpression");
				elAdd.addCDATA("${" + condition + "}");
			}
		}
		String outXml = doc.asXML();
		outXml = outXml.replace("xmlns:bpm=\"isperp\"",
				"xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"");
		return outXml;
	}

	public static Map<String, String> getDecisionConditions(String defXml,
			String nodeId) throws DocumentException {
		Map<String, String> map = new HashMap<>();
		defXml = defXml.replace(
				"xmlns=\"http://www.omg.org/spec/BPMN/20100524/MODEL\"",
				"xmlns:bpm='isperp'");
		Document doc = Dom4jUtils.loadXml(defXml);
		Element root = doc.getRootElement();

		List<Element> nodes = root.selectNodes("//sequenceFlow[@sourceRef='"
				+ nodeId + "']");
		for (Element el : nodes) {
			String id = el.attributeValue("id");
			String condition = "";
			Element conditionNode = el.element("conditionExpression");
			if (conditionNode != null) {
				condition = conditionNode.getText().trim();
				int l = condition.length();
				condition = condition.substring(2, l - 1);
			}
			map.put(id, condition);
		}
		return map;
	}

}
